package com.smartinput.voice.application.service

import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Service
import java.net.http.HttpClient
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicBoolean

@Service
class AliyunWebSocketSpeechRecognitionService {

    private val logger = LoggerFactory.getLogger(AliyunWebSocketSpeechRecognitionService::class.java)
    private val objectMapper = jacksonObjectMapper()
    private val httpClient = HttpClient.newHttpClient()
    
    // 为每个用户/设备创建独立的WebSocket客户端
    private val voiceAliyunClients = ConcurrentHashMap<String/*trace id*/, AliyunWebSocketClient>()
    
    // 为每个用户/设备创建独立的会话状态管理
    private val sessionStates = ConcurrentHashMap<String/*trace id*/, SessionState>()

    @Autowired
    private lateinit var aliyunTokenService: AliyunTokenService

    @Value("\${aliyun.speech.appkey:}")
    private lateinit var appKey: String

    @Value("\${aliyun.speech.region:cn-shanghai}")
    private lateinit var region: String

    // 会话状态数据类
    data class SessionState(
        var isSessionStarted: AtomicBoolean = AtomicBoolean(false),
        var isWaitingForTranscriptionStarted: AtomicBoolean = AtomicBoolean(false),
        var currentTaskId: String? = null,
        var currentMessageId: String? = null,
        var recognitionResultCallback: ((SpeechRecognitionManager.RecognitionResult) -> Unit)? = null
    )

    /**
     * 设置异步识别结果回调
     * 当有识别结果时，会通过这个回调函数通知
     */
    fun setRecognitionResultCallback(traceId: String, callback: (SpeechRecognitionManager.RecognitionResult) -> Unit) {
        val sessionState = getOrCreateSessionState(traceId)
        sessionState.recognitionResultCallback = callback
        logger.debug("已为traceId=$traceId 设置异步识别结果回调")
    }

    /**
     * 清除异步识别结果回调
     */
    fun clearRecognitionResultCallback(traceId: String) {
        val sessionState = sessionStates[traceId]
        if (sessionState != null) {
            sessionState.recognitionResultCallback = null
            logger.debug("已为traceId=$traceId 清除异步识别结果回调")
        }
    }

    /**
     * 发送音频数据进行异步识别
     * 注意：这是异步操作，不等待识别结果
     * 识别结果将通过回调函数异步返回
     */
    fun sendAudioData(traceId: String, audioData: ByteArray): SpeechRecognitionManager.RecognitionResult {
        return try {
            logger.debug("发送音频数据到阿里云，traceId=$traceId, 音频大小: ${audioData.size} bytes")

            // 获取会话状态
            val currentSessionState = getOrCreateSessionState(traceId)
            
            // 检查会话状态，如果未开始且不在等待TranscriptionStarted，则自动初始化
            if (!isSessionActive(traceId) && !currentSessionState.isWaitingForTranscriptionStarted.get()) {
                logger.info("会话未开始，自动初始化会话，traceId=$traceId")
                currentSessionState.isWaitingForTranscriptionStarted.set(true)
                if (!startSession(traceId)) {
                    currentSessionState.isWaitingForTranscriptionStarted.set(false)
                    return SpeechRecognitionManager.RecognitionResult(
                        text = "",
                        confidence = 0.0,
                        success = false,
                        error = "会话初始化失败"
                    )
                }
            }

            // 获取当前连接
            val client = this.voiceAliyunClients[traceId]
            if (client == null || !client.isConnected()) {
                logger.error("WebSocket连接不可用，traceId=$traceId")
                resetConnectionAndSession(traceId)
                return SpeechRecognitionManager.RecognitionResult(
                    text = "",
                    confidence = 0.0,
                    success = false,
                    error = "WebSocket连接不可用"
                )
            }
            
            // 获取会话状态
            val sessionState = sessionStates[traceId]
            if (sessionState == null) {
                logger.error("会话状态不存在，traceId=$traceId")
                return SpeechRecognitionManager.RecognitionResult(
                    text = "",
                    confidence = 0.0,
                    success = false,
                    error = "会话状态不存在"
                )
            }
            
            // 异步发送音频数据到阿里云
            if (sessionState.currentTaskId != null && sessionState.currentMessageId != null) {
                // 检查语音识别是否已开始
                if (!client.isTranscriptionStarted()) {
                    logger.warn("语音识别尚未开始，等待TranscriptionStarted消息，traceId=$traceId")
                    return SpeechRecognitionManager.RecognitionResult(
                        text = "",
                        confidence = 0.0,
                        success = false,
                        error = "语音识别尚未开始，请等待TranscriptionStarted消息"
                    )
                }
                
                client.sendAudioData(sessionState.currentTaskId!!, sessionState.currentMessageId!!, audioData)
                logger.debug("音频数据已发送到阿里云，traceId=$traceId")
                
                // 返回成功状态，不等待识别结果
                // 识别结果将通过回调函数异步返回
                return SpeechRecognitionManager.RecognitionResult(
                    text = "",
                    confidence = 0.0,
                    success = true // 发送成功即可，结果会异步返回
                )
            } else {
                logger.error("会话参数无效，traceId=$traceId")
                return SpeechRecognitionManager.RecognitionResult(
                    text = "",
                    confidence = 0.0,
                    success = false,
                    error = "会话参数无效"
                )
            }
            
        } catch (e: Exception) {
            logger.error("发送音频数据失败，traceId=$traceId", e)
            // 连接出错时，安全地重置连接和会话状态
            safeResetConnection(traceId)
            SpeechRecognitionManager.RecognitionResult(
                text = "",
                confidence = 0.0,
                success = false,
                error = e.message
            )
        }
    }

    /**
     * 开始语音识别会话
     * 初始化WebSocket连接并发送StartTranscription指令
     */
    fun startSession(traceId: String): Boolean {
        return try {
            logger.info("开始初始化语音识别会话，traceId=$traceId")

            // 获取Token
            val currentToken = getToken()
            if (currentToken == null) {
                logger.error("无法获取阿里云Token，会话初始化失败")
                return false
            }

            // 重置之前的会话状态
            resetConnectionAndSession(traceId)

            // 获取或创建WebSocket连接
            val client = getOrCreateConnection(traceId, currentToken)

            // 获取会话状态
            val sessionState = getOrCreateSessionState(traceId)

            // 生成新的taskId和messageId，并验证格式
            sessionState.currentTaskId = generateTaskId()
            sessionState.currentMessageId = generateMessageId()
            
            // 验证生成的ID格式
            if (!validateId(sessionState.currentTaskId!!, "TaskId") || 
                !validateId(sessionState.currentMessageId!!, "MessageId")) {
                logger.error("ID格式验证失败，会话初始化终止")
                return false
            }

            logger.info("开始新的语音识别会话，traceId=$traceId, taskId=${sessionState.currentTaskId}, messageId=${sessionState.currentMessageId}")

            // 发送StartTranscription指令
            client.sendStartTranscription(sessionState.currentTaskId!!, sessionState.currentMessageId!!)

            logger.info("语音识别会话初始化成功，traceId=$traceId")
            true

        } catch (e: Exception) {
            logger.error("语音识别会话初始化失败，traceId=$traceId", e)
            safeResetConnection(traceId)
            false
        }
    }

    /**
     * 检查会话是否已开始
     */
    fun isSessionActive(traceId: String): Boolean {
        val sessionState = sessionStates[traceId]
        return sessionState?.isSessionStarted?.get() == true && this.voiceAliyunClients[traceId]?.isConnected() == true
    }

    /**
     * 获取当前会话状态
     */
    fun getSessionStatus(traceId: String): Map<String, Any> {
        val sessionState = sessionStates[traceId]
        return mapOf(
            "isSessionStarted" to (sessionState?.isSessionStarted?.get() ?: false),
            "isConnected" to (this.voiceAliyunClients[traceId]?.isConnected() ?: false),
            "currentTaskId" to (sessionState?.currentTaskId ?: ""),
            "currentMessageId" to (sessionState?.currentMessageId ?: "")
        )
    }

    /**
     * 获取或创建会话状态
     */
    private fun getOrCreateSessionState(traceId: String): SessionState {
        return sessionStates.getOrPut(traceId) { SessionState() }
    }

    private fun getToken(): String? {
        return try {
            logger.info("获取阿里云Token...")
            val token = aliyunTokenService.getValidToken()
            logger.info("阿里云Token获取成功")
            token
        } catch (e: Exception) {
            logger.error("获取Token异常", e)
            null
        }
    }

    private fun currentConnection(traceId: String): AliyunWebSocketClient? {
        return this.voiceAliyunClients[traceId]
    }
    
    private fun getOrCreateConnection(traceId: String, token: String): AliyunWebSocketClient {
        synchronized(connectionLock) {
            // 如果连接存在且有效，直接返回
            if (currentConnection(traceId)?.isConnected() == true) {
                logger.debug("复用现有WebSocket连接，traceId=$traceId")
                return currentConnection(traceId)!!
            }

            // 创建新连接
            logger.info("创建新的WebSocket连接，traceId=$traceId")
            val connectionId = UUID.randomUUID().toString()
            logger.info("创建WebSocket连接: $connectionId, traceId=$traceId")

            val newConnection = AliyunWebSocketClient(
                token = token,
                appKey = appKey,
                onMessage = { message ->
                    logger.debug("收到WebSocket消息，traceId=$traceId: $message")
                },
                onError = { error ->
                    logger.error("WebSocket错误，traceId=$traceId: $error")
                    // 避免在错误回调中重置连接，可能导致递归
                    logger.warn("WebSocket错误，但不重置连接以避免递归，traceId=$traceId")
                },
                onClose = { code, reason ->
                    logger.info("WebSocket连接关闭，traceId=$traceId: $code, reason=$reason")
                    // 清理连接引用和会话状态
                    synchronized(connectionLock) {
                        voiceAliyunClients.remove(traceId)
                        sessionStates.remove(traceId)
                        logger.info("已清理WebSocket连接引用和会话状态，traceId=$traceId")
                    }
                },
                onRecognitionResult = { result, isFinal ->
                    // 异步处理识别结果
                    logger.info("收到异步识别结果，traceId=$traceId: $result, isFinal: $isFinal")

                    val recognitionResult = SpeechRecognitionManager.RecognitionResult(
                        text = result,
                        confidence = if (isFinal) 0.9 else 0.7,
                        success = true,
                        isFinal = isFinal // 传递是否为最终结果
                    )

                    // 调用对应traceId的回调函数
                    val sessionState = sessionStates[traceId]
                    sessionState?.recognitionResultCallback?.invoke(recognitionResult)
                },
                onTranscriptionStarted = {
                    // 收到TranscriptionStarted事件，设置会话状态为已开始
                    val sessionState = sessionStates[traceId]
                    sessionState?.isWaitingForTranscriptionStarted?.set(false)
                    sessionState?.isSessionStarted?.set(true)
                    logger.info("收到TranscriptionStarted事件，会话已开始，traceId=$traceId")
                }
            )

            // 连接WebSocket
            if (!newConnection.connect()) {
                throw RuntimeException("WebSocket连接失败")
            }

            this.voiceAliyunClients[traceId] = newConnection

            return newConnection
        }
    }

    private fun resetConnectionAndSession(traceId: String) {
        synchronized(connectionLock) {
            try {
                currentConnection(traceId)?.disconnect()
            } catch (e: Exception) {
                logger.warn("断开连接时发生异常，traceId=$traceId", e)
            }
            
            // 重置会话状态
            val sessionState = sessionStates[traceId]
            if (sessionState != null) {
                sessionState.isSessionStarted.set(false)
                sessionState.isWaitingForTranscriptionStarted.set(false)
                sessionState.currentTaskId = null
                sessionState.currentMessageId = null
            }
            
            logger.info("WebSocket连接和会话状态已重置，traceId=$traceId")
        }
    }

    /**
     * 安全地重置连接，避免递归调用
     */
    private fun safeResetConnection(traceId: String) {
        try {
            synchronized(connectionLock) {
                val connection = currentConnection(traceId)
                if (connection != null) {
                    val sessionState = sessionStates[traceId]
                    if (sessionState != null) {
                        sessionState.isSessionStarted.set(false)
                        sessionState.isWaitingForTranscriptionStarted.set(false)
                        sessionState.currentTaskId = null
                        sessionState.currentMessageId = null
                    }
                    logger.info("安全重置WebSocket连接状态，traceId=$traceId")

                    // 在同步块外执行断开连接操作
                    try {
                        connection.disconnect()
                    } catch (e: Exception) {
                        logger.warn("断开连接时发生异常，traceId=$traceId", e)
                    }
                }
            }
        } catch (e: Exception) {
            logger.error("安全重置连接时发生异常，traceId=$traceId", e)
        }
    }

    /**
     * 结束当前语音识别会话
     * 应该在用户停止录音或需要结束会话时调用
     */
    fun stopCurrentSession(traceId: String): Boolean {
        return try {
            synchronized(connectionLock) {
                val client = currentConnection(traceId)
                val sessionState = sessionStates[traceId]
                
                if (client?.isSessionStarted() == true && client.isConnected() && 
                    sessionState?.currentTaskId != null && sessionState.currentMessageId != null) {
                    logger.info("结束语音识别会话，traceId=$traceId")

                    // 发送StopTranscription指令
                    client.sendStopTranscription(sessionState.currentTaskId!!, sessionState.currentMessageId!!)

                    // 注意：不等待最终结果，结果会通过异步回调返回
                    logger.info("已发送StopTranscription指令，traceId=$traceId")

                    // 重置会话状态
                    client.stopSession()
                    sessionState.currentTaskId = null
                    sessionState.currentMessageId = null

                    true
                } else {
                    logger.debug("没有活跃的语音识别会话，traceId=$traceId")
                    false
                }
            }
        } catch (e: Exception) {
            logger.error("结束会话失败，traceId=$traceId", e)
            resetConnectionAndSession(traceId)
            false
        }
    }

    /**
     * 清理指定traceId的所有资源
     */
    fun cleanupSession(traceId: String) {
        try {
            logger.info("清理会话资源，traceId=$traceId")
            
            // 停止会话
            stopCurrentSession(traceId)
            
            // 断开连接
            val client = voiceAliyunClients.remove(traceId)
            client?.disconnect()
            
            // 清理会话状态
            sessionStates.remove(traceId)
            
            logger.info("会话资源清理完成，traceId=$traceId")
        } catch (e: Exception) {
            logger.error("清理会话资源失败，traceId=$traceId", e)
        }
    }

    private fun generateTaskId(): String {
        val taskId = UUID.randomUUID().toString().replace("-", "")
        logger.trace("生成TaskId: $taskId (长度: ${taskId.length})")
        return taskId
    }

    private fun generateMessageId(): String {
        val messageId = UUID.randomUUID().toString().replace("-", "")
        logger.trace("生成MessageId: $messageId (长度: ${messageId.length})")
        return messageId
    }
    
    /**
     * 验证ID格式是否符合阿里云要求（32位十六进制字符）
     */
    private fun validateId(id: String, idType: String): Boolean {
        if (id.length != 32) {
            logger.error("❌ ${idType}长度错误: 期望32位，实际${id.length}位 - $id")
            return false
        }
        
        if (!id.matches(Regex("[a-fA-F0-9]{32}"))) {
            logger.error("❌ ${idType}格式错误: 必须是32位十六进制字符 - $id")
            return false
        }
        
        logger.trace("✅ ${idType}格式验证通过: $id")
        return true
    }
    

    
    // 获取阿里云AppKey
    fun getAppKey(): String {
        return appKey
    }
    
    /**
     * 优化音频数据发送：分块发送，避免发送过快
     */
    private fun sendAudioDataOptimized(client: AliyunWebSocketClient, audioData: ByteArray) {
        try {
            // 跳过WAV文件头（44字节），只发送音频数据
            val audioDataStart = 44
            val actualAudioData = if (audioData.size > audioDataStart) {
                audioData.sliceArray(audioDataStart until audioData.size)
            } else {
                audioData
            }

            logger.debug("开始发送音频数据，跳过文件头后大小: ${actualAudioData.size} bytes")

            // 分块发送音频数据，每块4KB
            val chunkSize = 4096
            var offset = 0

            while (offset < actualAudioData.size) {
                val chunk = if (offset + chunkSize < actualAudioData.size) {
                    actualAudioData.sliceArray(offset until offset + chunkSize)
                } else {
                    actualAudioData.sliceArray(offset until actualAudioData.size)
                }

                // 注意：这里需要taskId和messageId，但这个方法已经废弃
                // 使用新的sendAudioData方法
                logger.debug("跳过音频数据发送，使用新的sendAudioData方法")
                offset += chunkSize

                // 添加短暂延迟，避免发送过快
                if (offset < actualAudioData.size) {
                    Thread.sleep(10)
                }
            }

            logger.debug("音频数据分块发送完成，总块数: ${(actualAudioData.size + chunkSize - 1) / chunkSize}")

        } catch (e: Exception) {
            logger.error("音频数据发送失败", e)
            throw e
        }
    }

    // WebSocket连接管理锁
    private val connectionLock = Any()

} 